#ifndef __MY_LOG_LOGGER
#define __MY_LOG_LOGGER
#include <vector>

#include "sink.hpp"
#include "format.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>
#include "looper.hpp"
#include <unordered_map>
namespace mylog
{

    class Logger
    {
        public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string& logname,LogLevel::value level,Formatter::ptr& formatter,std::vector<LogSink::ptr> sinks)
        :_name(logname),_limit_level(level),_formatter(formatter),_sinks(sinks.begin(),sinks.end())
        {

        }
        const std::string& GetName()
        {
            return _name;
        }
        void serialize(const std::string& file, size_t line,LogLevel::value level,char* str)
        {
            LogMsg msg(line, level, file, _name, str);
            std::stringstream ss;
            _formatter->format(ss,msg);
            log(ss.str().c_str(),ss.str().size());
        }
        void debug(const std::string& file, size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::DEBUG < _limit_level)
            {
                return;
            }
            va_list ap;
            va_start(ap,fmt);
            char* str;
            int ret = vasprintf(&str, fmt.c_str(), ap);
            if(ret == -1)
            {
                std::cout <<"vasprintf err"<<std::endl;
                return;
            }
            va_end(ap);
            serialize(file, line, LogLevel::value::DEBUG,str);

        }
        void info(const std::string& file, size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::INFO < _limit_level)
            {
                return;
            }
            va_list ap;
            va_start(ap,fmt);
            char* str;
            int ret = vasprintf(&str, fmt.c_str(), ap);
            if(ret == -1)
            {
                std::cout <<"vasprintf err"<<std::endl;
                return;
            }
            va_end(ap);
            serialize(file, line, LogLevel::value::INFO,str);

        }
        void warn(const std::string& file, size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::WARN < _limit_level)
            {
                return;
            }
            va_list ap;
            va_start(ap,fmt);
            char* str;
            int ret = vasprintf(&str, fmt.c_str(), ap);
            if(ret == -1)
            {
                std::cout <<"vasprintf err"<<std::endl;
                return;
            }
            va_end(ap);
            serialize(file, line, LogLevel::value::WARN,str);

        }
        void error(const std::string& file, size_t line,const std::string& fmt,...)
        {
            if(LogLevel::value::ERROR < _limit_level)
            {
                return;
            }
            va_list ap;
            va_start(ap,fmt);
            char* str;
            int ret = vasprintf(&str, fmt.c_str(), ap);
            if(ret == -1)
            {
                std::cout <<"vasprintf err"<<std::endl;
                return;
            }
            va_end(ap);
            serialize(file, line, LogLevel::value::ERROR, str);

        }
        void fatal(const std::string& file, size_t line,const std::string& fmt,...)
        {
            //std::cout <<"fatal in" << std::endl;
            if(LogLevel::value::FATAL < _limit_level)
            {
                return;
            }
            va_list ap;
            va_start(ap,fmt);
            char* str;
            int ret = vasprintf(&str, fmt.c_str(), ap);
            if(ret == -1)
            {
                std::cout <<"vasprintf err"<<std::endl;
                return;
            }
            va_end(ap);
            serialize(file, line, LogLevel::value::FATAL,str);

        }
        protected:
            virtual void log(const std::string& data, size_t len) = 0;
            std::string _name;
            std::mutex _mutex;
            std::atomic<LogLevel::value> _limit_level;
            Formatter::ptr _formatter;
            std::vector<LogSink::ptr> _sinks;
    };

    class SyncLogger : public Logger
    {
        public:
            SyncLogger(const std::string& logname,LogLevel::value level,Formatter::ptr& formatter,std::vector<LogSink::ptr>& sinks)
            :Logger(logname,level,formatter,sinks)
            {

            }

        protected:
            void log(const std::string& data, size_t len)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                if(_sinks.empty())
                {
                    return;
                }
                for(const auto& sink : _sinks)
                {
                    sink->log(data.c_str(), len);
                }
            }
    };

    class AsyncLogger : public Logger
    {
        public:
 
            AsyncLogger(const std::string& logname,LogLevel::value level,Formatter::ptr& formatter,std::vector<LogSink::ptr>& sinks,AsyncType type)
            :Logger(logname,level,formatter,sinks),
            _looper(std::make_shared<Asynclooper>(std::bind(&AsyncLogger::realLog,this,std::placeholders::_1),type))
            {
                std::cout << "async build success" << std::endl;
            }

            void realLog(Buffer& buff)
            {
                if(_sinks.empty()) return;
                for(auto& sink : _sinks)
                {
                    sink->log(buff.begin(), buff.ReadAbleSIze());
                }
            }
        protected:
            void log(const std::string& data, size_t len)
            {
                _looper->push(data.c_str(),len);
            }
        private:
            Asynclooper::ptr _looper;
    };

    enum class LoggerType
    {
        LOGGER_SYNC,
        LOGGER_ASYNC
    };
    class LoggerBuilder
    {
        public:
            LoggerBuilder():_logger_type(LoggerType::LOGGER_SYNC),_limite_level(LogLevel::value::DEBUG),_lopper_type(AsyncType::ASYNC_SAFE)
            {}
            void BuildLoggerType(const LoggerType& type)
            {
                _logger_type = type;
            }
            void BuildLoggerName(const std::string& logname)
            {
                _logname = logname;
            }
            void BuildEnableUnsafeAsync()
            {
                _lopper_type = AsyncType::ASYNC_UNSAFE;
            }
            void BuildLoggerLevel(const LogLevel::value& level)
            {
                _limite_level = level;
            }
            void BuildLoggerFormat(const std::string& format) 
            {
                _formatter = std::make_shared<Formatter>(format);
            }
            template<typename SinkType,typename ...Args>
            void BuildLoggerSink(Args&& ...args)
            {
                LogSink::ptr tmpsink = Factory::creat<SinkType>(std::forward<Args>(args)...);
                _sinks.push_back(tmpsink);
            
            }
            virtual Logger::ptr Build() = 0;
        protected:
            AsyncType _lopper_type;
            LoggerType _logger_type;
            std::string _logname;
            LogLevel::value _limite_level;
            Formatter::ptr _formatter;
            std::vector<LogSink::ptr> _sinks;
    };
    class LocalLoggerBuilder : public LoggerBuilder
    {
        public :
            Logger::ptr Build()
            {
                assert(_logname.empty() == false);
                if(_formatter.get() == nullptr)
                {
                    _formatter = std::make_shared<Formatter>();
                }
                if(_sinks.empty())
                {
                    BuildLoggerSink<StdoutSink>();
                }
                if(_logger_type == LoggerType::LOGGER_ASYNC)
                {
                    std::cout << "build succ" << std::endl;
                    return std::make_shared<AsyncLogger>(_logname,_limite_level,_formatter,_sinks,_lopper_type);
                }
                else if(_logger_type == LoggerType::LOGGER_SYNC)
                {
                    return std::make_shared<SyncLogger>(_logname,_limite_level,_formatter,_sinks);
                }
                return nullptr;
            }
    };
    class LoggerManager
    {
        public:
            static LoggerManager& getInstance()
            {
                static LoggerManager manager;
                return manager;
            }
            void addlogger(Logger::ptr& log)
            {
                if(haslogger(log->GetName()) == true) return;
                std::unique_lock<std::mutex> lock(_mutex);
                _loggers[log->GetName()] = log;
            }
            bool haslogger(const std::string& name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _loggers.find(name);
                if(it == _loggers.end())
                {
                    return false;
                }
                return true;

            }
            Logger::ptr getLogger(const std::string& name)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _loggers.find(name);
                if(it == _loggers.end())
                {
                    return nullptr;
                }
                return it->second;
            }
            Logger::ptr rootLogger()
            {
                return _root_logger;
            }            

        private:
            LoggerManager()
            {
                std::string rname = "root";
                std::shared_ptr<LoggerBuilder> build(new LocalLoggerBuilder());
                build->BuildLoggerName(rname);
                _root_logger = build->Build();
                addlogger(_root_logger);
            }
            std::mutex _mutex;
            Logger::ptr _root_logger;
            std::unordered_map<std::string,Logger::ptr> _loggers;
    };
    class GloubLoggerBuilder : public LoggerBuilder
    {
        public :
            Logger::ptr Build()
            {
                assert(_logname.empty() == false);
                if(_formatter.get() == nullptr)
                {
                    _formatter = std::make_shared<Formatter>();
                }
                if(_sinks.empty())
                {
                    BuildLoggerSink<StdoutSink>();
                }
                Logger::ptr logger;
                if(_logger_type == LoggerType::LOGGER_ASYNC)
                {
                    logger = std::make_shared<AsyncLogger>(_logname,_limite_level,_formatter,_sinks,_lopper_type);
                }
                else if(_logger_type == LoggerType::LOGGER_SYNC)
                {
                    logger = std::make_shared<SyncLogger>(_logname,_limite_level,_formatter,_sinks);
                }
                LoggerManager::getInstance().addlogger(logger);
                return logger;
            }
    };


}

#endif